//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

// Includes
#include <mattise.h>
#include <stdint.h>
#include <string.h>
#include <vfs/vfs.h>
#include <mem/util.h>
#include <mem/heap.h>
#include <kernio/io.h>

// TODO: Implement this in a much better way. That will entail
// a massive rewrite!

// Defines

// ...

// Variables

#if VFS_USE_LINKED_LISTS
	struct vfs_node* mount_tab = 0;
	struct vfs_desc* desc_tab = 0;
#else
	struct vfs_node* mount_tab; //[MAXMOUNTENTRIES];
	struct vfs_desc* desc_tab; //[MAXDESCENTRIES];
#endif

// Functions

// sets up the VFS for use by anything
void kInitializeVFS()
{
#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	mount_tab = (struct vfs_node*) malloc( (MAXMOUNTENTRIES + 1) * sizeof( struct vfs_node ) );
	desc_tab = (struct vfs_desc*) malloc( (MAXDESCENTRIES + 1) * sizeof( struct vfs_node ) );

	// make all entries invalid
	int i;
	for( i = 0; i < MAXMOUNTENTRIES; i++ )
		mount_tab[i].mounted = 0;
#endif
}

// mounts a new filesystem in the VFS - maps dir onto device
int vfsmount( int persistent /* MUST BE ZERO IN 3.0 */, const char* device, const char* dir )
{
	// check that persistent is 0 because we don't yet handle it
	// also check for valid pointers
	if( persistent != 0 )
		return -1;
	if( !device || !dir )
		return -1;

	dprintf( "mounting '%s' --> '%s'\n", dir, device );

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	// find a valid entry
	int i;
	for( i = 0; i < MAXMOUNTENTRIES; i++ )
		if( mount_tab[i].mounted == 0 )
			break;
	if( i == MAXMOUNTENTRIES )
		return -1;
	memcpy( mount_tab[i].device, device, strlen( device ) + 1 );
	memcpy( mount_tab[i].prefix, dir, strlen( dir ) + 1 );
	mount_tab[i].perms = 0;
	mount_tab[i].mounted = 1;
#endif

	return 0;
}

// unmounts a filesystem in the VFS
int vfsunmount( const char* dir )
{
	// parameter checks
	if( !dir )
		return -1;

	dprintf( "unmounting '%s'\n", dir );

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	// find a valid entry
	int i;
	for( i = 0; i < MAXMOUNTENTRIES; i++ )
		if( strncmp( mount_tab[i].prefix, dir, strlen( mount_tab[i].prefix ) ) == 0 )
			break;
	if( i == MAXMOUNTENTRIES )
		return -1;
	mount_tab[i].mounted = 0;
#endif
	return 0;
}

// file i/o stuff
int vfs_open( const char* fn, int UNUSED(mode) )
{
	// check for valid filename
	if( !fn )
		return -1;

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	// find a valid entry in the descriptor table
	int i;
	for( i = 0; i < MAXDESCENTRIES; i++ )
		if( desc_tab[i].used == 0 )
			break;
	if( i == MAXDESCENTRIES )
		return -1;

	// find the entry in the mount table
	int j;
	for( j = 0; j < MAXMOUNTENTRIES; j++ )
	{
		// skip processing if it's not worth trying
		if( mount_tab[j].prefix[0] == 0 )
			continue;

		// otherwise check for a valid prefix
		if( ( strncmp( mount_tab[j].prefix, fn, strlen( mount_tab[j].prefix ) ) == 0 ) && mount_tab[j].mounted )
			break;
	}
	if( j == MAXMOUNTENTRIES )
		return -1;

	// i now holds a usable descriptor, j the mount point
	// we need to replace the prefix in fn with the actual device path...
	char* tempbuff = (char*) malloc( strlen( fn ) + strlen( mount_tab[j].device ) );
	strcpy( tempbuff, mount_tab[j].device );
	if( tempbuff[strlen(tempbuff)-1] != '/' )
		strcat( tempbuff, "/" );
	strcat( tempbuff, fn + (strlen( mount_tab[j].prefix )) );
	
	// open it!
	desc_tab[i].fd = sys_open( tempbuff, 0 );
	free( tempbuff ); // won't be needed anymore
	if( desc_tab[i].fd == -1 )
		return -1;
	else
	{
		desc_tab[i].node = &mount_tab[j];
		desc_tab[i].used = 1;
		return i;
	}
#endif
}
int vfs_read( int fd, const char* buff, size_t len )
{
	// sanity checks
	if( !buff || !len || (fd < 0 || fd > MAXDESCENTRIES) )
		return -1;

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	if( desc_tab[fd].used == 0 || desc_tab[fd].fd == -1 )
		return -1;
	return sys_read( desc_tab[fd].fd, buff, len );
#endif
	return 0;
}
int vfs_write( int fd, char* buff, size_t len )
{
	// sanity checks
	if( !buff || !len || (fd < 0 || fd > MAXDESCENTRIES) )
		return -1;

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	if( desc_tab[fd].used == 0 || desc_tab[fd].fd == -1 )
		return -1;
	return sys_write( desc_tab[fd].fd, buff, len );
#endif
	return 0;
}
int vfs_seek( int fd, size_t off, int32_t set )
{
	// sanity checks
	if( (fd < 0 || fd > MAXDESCENTRIES) )
		return -1;

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	if( desc_tab[fd].used == 0 || desc_tab[fd].fd == -1 )
		return -1;
	return sys_seek( desc_tab[fd].fd, off, set );
#endif
	return 0;
}
int vfs_stat( int fd, struct stat* buff )
{
	// sanity checks
	if( !buff || (fd < 0 || fd > MAXDESCENTRIES) )
		return -1;

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	if( desc_tab[fd].used == 0 || desc_tab[fd].fd == -1 )
		return -1;
	return sys_stat( desc_tab[fd].fd, buff );
#endif
	return 0;
}
int vfs_ioctl( int fd, int action, int data )
{
	// sanity checks
	if( (fd < 0 || fd > MAXDESCENTRIES) )
		return -1;

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	if( desc_tab[fd].used == 0 || desc_tab[fd].fd == -1 )
		return -1;
	return sys_ioctl( desc_tab[fd].fd, action, data );
#endif
	return 0;
}
int vfs_close( int fd )
{
	// sanity checks
	if( (fd < 0 || fd > MAXDESCENTRIES) )
		return -1;

#if VFS_USE_LINKED_LISTS
	// not implemented yet
#else
	if( desc_tab[fd].used == 0 || desc_tab[fd].fd == -1 )
		return -1;
	int ret = sys_close( desc_tab[fd].fd );
	desc_tab[fd].used = 0;
	return ret;
#endif
	return 0;
}

// cleans up the VFS (and writes any changes to the mounttab - NOT IN 3.0)
int kCleanUpVFS()
{
	// nothing to do, so success
	return 0;
}
